﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Dycox.Linq
{
    [Serializable, DebuggerDisplay("Count={Count}, UseDefaultOrder={UseDefaultOrder}")]
    public sealed class SortOrderSettings : ICloneable
    {
        List<SortOrderEntry> _orders;

        public SortOrderSettings()
        {
            _orders = new List<SortOrderEntry>(5);
        }

        public SortOrderSettings(string expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            var list = Parse(expression);

            if (list == null)
                throw new ArgumentException(SR.GetString("invalid_expression"), expression);

            _orders = list;
        }

        internal bool HasAnyOrder
        {
            get { return _orders.Any(o => o.Order != SortOrder.None); }
        }

        public SortOrder this[string key]
        {
            get
            {
                if (key == null)
                    throw new ArgumentNullException("key");

                SortOrderEntry entry = _orders.FirstOrDefault(o => o.Key == key);
                if (entry != null) return entry.Order;
                else return SortOrder.None;
            }
            set
            {
                if (key == null)
                    throw new ArgumentNullException("key");

                foreach (var entry in _orders)
                {
                    if (entry.Key == key)
                    {
                        entry.Order = value;
                        return;
                    }
                }

                _orders.Add(new SortOrderEntry(key, value));
            }
        }

        public SortOrder this[int index]
        {
            get
            {
                if (index < 0 || index >= _orders.Count)
                    throw new ArgumentOutOfRangeException("index");

                return _orders[index].Order;
            }
        }

        public int Count
        {
            get { return _orders.Count; }
        }

        public void Clear()
        {
            _orders.Clear();
        }

        //static MethodInfo[] QueryableOrderMethods;

        //private static void EnsureQueryableMethods()
        //{
        //    if (QueryableOrderMethods == null)
        //    {
        //        string[] names = new string[] { "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending" };
        //        int length = names.Length;
        //        QueryableOrderMethods = new MethodInfo[length];

        //        foreach (MethodInfo method in typeof(System.Linq.Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static))
        //        {
        //            int index = Array.IndexOf<string>(names, method.Name);
        //            if (index >= 0)
        //            {
        //                if (method.GetParameters().Length == 2)
        //                    QueryableOrderMethods[index] = method;
        //            }
        //        }
        //    }
        //}

        //public event GetDefaultSortOrderEventHandler GetDefaultSort;

        //protected virtual void OnGetDefaultSortEntries(GetDefaultSortOrderEventArgs e)
        //{
        //    if (GetDefaultSort != null)
        //        GetDefaultSort(this, e);
        //}

        public SortOrderSettings Clone()
        {
            SortOrderSettings obj = new SortOrderSettings();

            foreach (var entry in this._orders)
                obj._orders.Add(new SortOrderEntry(entry.Key, entry.Order));

            return obj;

        }
        
        object ICloneable.Clone()
        {
            return this.Clone();
        }

        internal SortOrderEntry[] GetEntries()
        {
            return _orders.Where(o => o.Order != SortOrder.None).ToArray();
        }

        public static readonly char[] Seperators = new char[] { ',', ';' };

        public static readonly string[] OrderClauses = new string[] { "ASC", "ASCENDING", "DESC", "DESCENDING" };
 
        private static List<SortOrderEntry> Parse(string text)
        {
            if (text == null) return null;

            string[] array = text.Split(Seperators, StringSplitOptions.RemoveEmptyEntries);

            List<SortOrderEntry> list = new List<SortOrderEntry>(array.Length);

            for (int i = 0; i < array.Length; i++ )
            {
                string value = array[i].Trim(), name;
                int p = value.LastIndexOf(' ');
                SortOrder order = SortOrder.Ascending;

                if (p > 0)
                {
                    name = value.Substring(p + 1).ToUpper();
                    if (OrderClauses.Contains(name))
                    {
                        if (name.StartsWith(OrderClauses[2]))
                            order = SortOrder.Descending;
                        name = value.Substring(0, p).Trim();
                    }
                    else
                        name = value;
                }
                else
                    name = value;

                list.Add(new SortOrderEntry(name, order));
            }

            return list;
        }
    }

    [Serializable, DebuggerDisplay("{Key}, {Order}")]
    internal class SortOrderEntry
    {
        public SortOrderEntry(string key, SortOrder order)
        {
            _key = key;
            _order = order;
        }

        public SortOrderEntry(string key)
            : this(key, SortOrder.None)
        {

        }

        private readonly string _key;

        public string Key
        {
            get { return _key; }
        }


        private SortOrder _order;

        public SortOrder Order
        {
            get { return _order; }
            set { _order = value; }
        }

    }
}
